﻿using EntityFramework.Extensions;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Data.Common;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Dynamic;
using System.Linq.Expressions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace DAL
{
    public class ApplicationDbService<TDbContext>
        where TDbContext : DbContext, new()
    {
        public TDbContext Context
        {
            get;
            private set;
        }

        public ApplicationDbService(TDbContext context)
        {
            this.Context = context;
        }

        /// <summary>
        /// 附加实体对象到DbContext上下文环境，使实体状态为Unchanged状态。
        /// </summary>
        /// <param name="entity">实体对象。</param>
        public virtual void Attach(object entity)
        {
            this.Context.Entry(entity).State = EntityState.Unchanged;
        }

        /// <summary>
        /// 附加实体对象到DbContext上下文环境，使实体状态为Detached状态。
        /// </summary>
        /// <param name="entity">实体对象。</param>
        public virtual void Detached(object entity)
        {
            this.Context.Entry(entity).State = EntityState.Detached;
        }

        /// <summary>
        /// 根据条件表达式判断是否存在当前值。
        /// </summary>
        /// <param name="predicate">条件表达式。</param>
        /// <returns>布尔值。</returns>
        public virtual bool Exists<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
        {
            return this.Context.Set<TEntity>().Any(predicate);
        }

        public virtual async Task<bool> ExistsAsync<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class
        {
            return await this.Context.Set<TEntity>().AnyAsync(predicate);
        }

        /// <summary>
        /// 添加实体。
        /// </summary>
        /// <param name="entity">实体对象。</param>
        public virtual int Add<TEntity>(TEntity entity) where TEntity : class
        {
            this.Context.Set<TEntity>().Add(entity);
            return this.Context.SaveChanges();
        }

        public virtual async Task<int> AddAsync<TEntity>(TEntity entity) where TEntity : class
        {
            this.Context.Set<TEntity>().Add(entity);
            return await this.Context.SaveChangesAsync();
        }

        /// <summary>
        /// 批量添加
        /// </summary>
        /// <param name="entities">实体集合。</param>
        public virtual int AddRange<TEntity>(IEnumerable<TEntity> entities) where TEntity : class
        {
            this.Context.Set<TEntity>().AddRange(entities);
            return this.Context.SaveChanges();
        }

        public virtual async Task<int> AddRangeAsync<TEntity>(IEnumerable<TEntity> entities) where TEntity : class
        {
            this.Context.Set<TEntity>().AddRange(entities);
            return await this.Context.SaveChangesAsync();
        }

        /// <summary>
        /// 更新实体。
        /// </summary>
        /// <param name="entity">实体对象。</param>
        public virtual int Update<TEntity>(TEntity entity) where TEntity : class
        {
            this.Context.Entry(entity).State = EntityState.Modified;
            return this.Context.SaveChanges();
        }

        public virtual async Task<int> UpdateAsync<TEntity>(TEntity entity) where TEntity : class
        {
            this.Context.Entry(entity).State = EntityState.Modified;
            return await this.Context.SaveChangesAsync();
        }

        /// <summary>
        /// 批量更新。
        /// </summary>
        /// <param name="where">条件表达式。</param>
        /// <param name="update">更新表达式。</param>
        public virtual void Update<TEntity>(Expression<Func<TEntity, bool>> where,
            Expression<Func<TEntity, TEntity>> update) where TEntity : class
        {
            this.Context.Set<TEntity>().Where(where).Update(update);
        }

        public virtual async Task<int> UpdateAsync<TEntity>(Expression<Func<TEntity, bool>> where,
            Expression<Func<TEntity, TEntity>> update) where TEntity : class
        {
            return await this.Context.Set<TEntity>().Where(where).UpdateAsync(update);
        }

        /// <summary>
        /// 删除实体。
        /// </summary>
        /// <param name="id">ID。</param>
        public virtual int Delete<TEntity>(Expression<Func<TEntity, bool>> where) where TEntity : class
        {
            return this.Context.Set<TEntity>().Where(where).Delete();
        }

        public virtual async Task<int> DeleteAsync<TEntity>(Expression<Func<TEntity, bool>> where) where TEntity : class
        {
            return await this.Context.Set<TEntity>().Where(where).DeleteAsync();
        }

        /// <summary>
        /// 查找带给定主键值的实体。
        /// </summary>
        /// <param name="keyValues">要查找的实体的主键值。</param>
        /// <returns>返回实体。</returns>
        public virtual TEntity Find<TEntity>(params object[] keyValues) where TEntity : class
        {
            return this.Context.Set<TEntity>().Find(keyValues);
        }

        public virtual async Task<TEntity> FindAsync<TEntity>(params object[] keyValues) where TEntity : class
        {
            return await this.Context.Set<TEntity>().FindAsync(keyValues);
        }

        /// <summary>
        /// 根据过滤条件获取实体。
        /// </summary>
        /// <param name="where">过滤条件。</param>
        /// <returns>返回实体。</returns>
        public virtual TEntity Get<TEntity>(Expression<Func<TEntity, bool>> where) where TEntity : class
        {
            return this.Context.Set<TEntity>().Where(where).FirstOrDefault();
        }

        public virtual async Task<TEntity> GetAsync<TEntity>(Expression<Func<TEntity, bool>> where) where TEntity : class
        {
            return await this.Context.Set<TEntity>().Where(where).FirstOrDefaultAsync();
        }

        /// <summary>
        /// 根据过滤条件获取实体，支持多个Include。
        /// </summary>
        /// <param name="where">过滤条件。</param>
        /// <param name="includes">includes条件集合。</param>
        /// <returns>返回实体。</returns>
        public virtual TEntity Get<TEntity>(Expression<Func<TEntity, bool>> where,
            params Expression<Func<TEntity, object>>[] includes) where TEntity : class
        {
            return this.Context.Set<TEntity>().Include(includes).Where(where).FirstOrDefault();
        }

        public virtual async Task<TEntity> GetAsync<TEntity>(Expression<Func<TEntity, bool>> where,
            params Expression<Func<TEntity, object>>[] includes) where TEntity : class
        {
            return await this.Context.Set<TEntity>().Include(includes).Where(where).FirstOrDefaultAsync();
        }

        /// <summary>
        /// 获取所有数据。
        /// </summary>
        /// <returns>返回所有数据。</returns>
        public virtual IEnumerable<TEntity> GetAll<TEntity>() where TEntity : class
        {
            return this.Context.Set<TEntity>().ToList();
        }

        public virtual async Task<IEnumerable<TEntity>> GetAllAsync<TEntity>() where TEntity : class
        {
            return await this.Context.Set<TEntity>().ToListAsync();
        }

        /// <summary>
        /// 获取所有数据，支持多个Include。
        /// </summary>
        /// <param name="includes">Include条件集合。</param>
        /// <returns>返回数据列表。</returns>
        public virtual IEnumerable<TEntity> GetAll<TEntity>(params Expression<Func<TEntity, object>>[] includes) where TEntity : class
        {
            return this.Context.Set<TEntity>().Include(includes).ToList();
        }

        public virtual async Task<IEnumerable<TEntity>> GetAllAsync<TEntity>(params Expression<Func<TEntity, object>>[] includes) where TEntity : class
        {
            return await this.Context.Set<TEntity>().Include(includes).ToListAsync();
        }

        /// <summary>
        /// 根据过滤条件获取数据，无分页。
        /// </summary>
        /// <param name="where">过滤条件。</param>
        /// <returns>返回数据列表。</returns>
        public virtual IList<TEntity> GetMany<TEntity>(Expression<Func<TEntity, bool>> where) where TEntity : class
        {
            return this.Context.Set<TEntity>().Where(where).ToList();
        }

        public virtual async Task<IList<TEntity>> GetManyAsync<TEntity>(Expression<Func<TEntity, bool>> where) where TEntity : class
        {
            return await this.Context.Set<TEntity>().Where(where).ToListAsync();
        }

        /// <summary>
        /// 根据过滤条件获取数据，无分页，支持多个Include。
        /// </summary>
        /// <param name="where">过滤条件。</param>
        /// <param name="includes">Include条件集合。</param>
        /// <returns>返回数据列表。</returns>
        public virtual IList<TEntity> GetMany<TEntity>(
            Expression<Func<TEntity, bool>> where,
            params Expression<Func<TEntity, object>>[] includes) where TEntity : class
        {
            return this.Context.Set<TEntity>().Include(includes).Where(where).ToList();
        }

        public virtual async Task<IList<TEntity>> GetManyAsync<TEntity>(Expression<Func<TEntity, bool>> where, params Expression<Func<TEntity, object>>[] includes) where TEntity : class
        {
            return await this.Context.Set<TEntity>().Include(includes).Where(where).ToListAsync();
        }

        /// <summary>
        /// 根据过滤条件获取数据，无分页，支持单个Include，指定返回数量。
        /// </summary>
        /// <param name="where">过滤条件。</param>
        /// <param name="orderBy">排序条件。</param>
        /// <param name="totalNumber">获取数据数量。</param>
        /// <param name="includes">Include条件集合。</param>
        /// <returns>返回数据列表。</returns>
        public virtual IList<TEntity> GetMany<TEntity>(Expression<Func<TEntity, bool>> where, string orderBy, int totalNumber, params Expression<Func<TEntity, object>>[] includes) where TEntity : class
        {
            return this.Context.Set<TEntity>().Include(includes).Where(where).OrderBy(orderBy).Take(totalNumber).ToList();
        }

        public virtual async Task<IList<TEntity>> GetManyAsync<TEntity>(Expression<Func<TEntity, bool>> where, string orderBy, int totalNumber, params Expression<Func<TEntity, object>>[] includes) where TEntity : class
        {
            return await this.Context.Set<TEntity>().Include(includes).Where(where).OrderBy(orderBy).Take(totalNumber).ToListAsync();
        }

        /// <summary>
        /// 根据过滤条件获取数据，无分页，支持单个Include，指定返回数量。
        /// </summary>
        /// <param name="where">过滤条件。</param>
        /// <param name="orderBy">排序条件。</param>
        /// <param name="totalNumber">获取数据数量。</param>
        /// <returns>返回数据列表。</returns>
        public virtual IList<TEntity> GetMany<TEntity>(Expression<Func<TEntity, bool>> where, string orderBy, int totalNumber) where TEntity : class
        {
            return this.Context.Set<TEntity>().Where(where).OrderBy(orderBy).Take(totalNumber).ToList();
        }

        public virtual async Task<IList<TEntity>> GetManyAsync<TEntity>(Expression<Func<TEntity, bool>> where, string orderBy, int totalNumber) where TEntity : class
        {
            return await this.Context.Set<TEntity>().Where(where).OrderBy(orderBy).Take(totalNumber).ToListAsync();
        }

        /// <summary>
        /// 根据过滤条件获取数据，无分页。
        /// </summary>
        /// <param name="where">过滤条件字符串。</param>
        /// <param name="whereParameters">过滤条件（SQL字符串表达式）。</param>
        /// <param name="orderBy">排序条件。</param>
        /// <returns>返回数据列表。</returns>
        public virtual IList<TEntity> GetMany<TEntity>(string where, object[] whereParameters, string orderBy) where TEntity : class
        {
            return this.Context.Set<TEntity>().Where(where, whereParameters).OrderBy(orderBy).ToList();
        }

        public virtual async Task<IList<TEntity>> GetManyAsync<TEntity>(string where, object[] whereParameters, string orderBy) where TEntity : class
        {
            return await this.Context.Set<TEntity>().Where(where, whereParameters).OrderBy(orderBy).ToListAsync();
        }

        /// <summary>
        /// 根据过滤条件获取数据，无分页，支持多个Include。
        /// </summary>
        /// <param name="where">过滤条件字符串。</param>
        /// <param name="whereParameters">过滤条件（SQL字符串表达式）。</param>
        /// <param name="orderBy">排序条件。</param>
        /// <param name="includes">Include条件集合。</param>
        /// <returns>返回数据列表。</returns>
        public virtual IList<TEntity> GetMany<TEntity>(string where, object[] whereParameters, string orderBy, string[] includes) where TEntity : class
        {
            return this.Context.Set<TEntity>().Include(includes).Where(where, whereParameters).OrderBy(orderBy).ToList();
        }

        public virtual async Task<IList<TEntity>> GetManyAsync<TEntity>(string where, object[] whereParameters, string orderBy, string[] includes) where TEntity : class
        {
            return await this.Context.Set<TEntity>().Include(includes).Where(where, whereParameters).OrderBy(orderBy).ToListAsync();
        }

        /// <summary>
        /// 根据过滤条件获取数据，无分页。
        /// </summary>
        /// <param name="where1">过滤条件字符串。</param>
        /// <param name="whereParameters">过滤条件（SQL字符串表达式）。</param>
        /// <param name="where2">过滤条件表达式。</param>
        /// <param name="orderBy">排序条件。</param>
        /// <returns>返回数据列表。</returns>
        public virtual IList<TEntity> GetMany<TEntity>(string where1, object[] whereParameters,
            Expression<Func<TEntity, bool>> where2, string orderBy) where TEntity : class
        {
            var exp = this.Context.Set<TEntity>().Where(where1, whereParameters);

            if (where2 != null)
            {
                exp = exp.Where(where2);
            }

            return exp.OrderBy(orderBy).ToList();
        }

        public virtual async Task<IList<TEntity>> GetManyAsync<TEntity>(string where1, object[] whereParameters,
            Expression<Func<TEntity, bool>> where2, string orderBy) where TEntity : class
        {
            var exp = this.Context.Set<TEntity>().Where(where1, whereParameters);

            if (where2 != null)
            {
                exp = exp.Where(where2);
            }

            return await exp.OrderBy(orderBy).ToListAsync();
        }

        /// <summary>
        /// 根据过滤条件获取数据，无分页，支持多个Include。
        /// </summary>
        /// <param name="where1">过滤条件字符串。</param>
        /// <param name="whereParameters">过滤条件（SQL字符串表达式）。</param>
        /// <param name="where2">过滤条件表达式。</param>
        /// <param name="orderBy">排序条件。</param>
        /// <param name="includes">Include条件集合。</param>
        /// <returns>返回数据列表。</returns>
        public virtual IList<TEntity> GetMany<TEntity>(string where1, object[] whereParameters,
            Expression<Func<TEntity, bool>> where2, string orderBy,
            params Expression<Func<TEntity, object>>[] includes) where TEntity : class
        {
            var exp = this.Context.Set<TEntity>().Include(includes).Where(where1, whereParameters);

            if (where2 != null)
            {
                exp = exp.Where(where2);
            }

            return exp.OrderBy(orderBy).ToList();
        }

        public virtual async Task<IList<TEntity>> GetManyAsync<TEntity>(string where1, object[] whereParameters,
            Expression<Func<TEntity, bool>> where2, string orderBy,
            params Expression<Func<TEntity, object>>[] includes) where TEntity : class
        {
            var exp = this.Context.Set<TEntity>().Include(includes).Where(where1, whereParameters);

            if (where2 != null)
            {
                exp = exp.Where(where2);
            }

            return await exp.OrderBy(orderBy).ToListAsync();
        }

        public virtual IList<TEntity> FindPageList<TEntity>(string orderField, bool isAsc, int pageSize, int pageIndex, out int total) where TEntity : class
        {
            string[] _order = orderField.Split(',');
            MethodCallExpression resultExp = null;
            var tempData = this.Context.Set<TEntity>().AsQueryable();
            foreach (string item in _order)
            {
                string _orderPart = item;
                _orderPart = Regex.Replace(_orderPart, @"\s+", " ");
                string[] _orderArry = _orderPart.Split(' ');
                string _orderField = _orderArry[0];
                bool sort = isAsc;
                if (_orderArry.Length == 2)
                {
                    isAsc = _orderArry[1].ToUpper() == "ASC" ? true : false;
                }
                var parameter = Expression.Parameter(typeof(TEntity), "t");
                var property = typeof(TEntity).GetProperty(_orderField);
                var propertyAccess = Expression.MakeMemberAccess(parameter, property);
                var orderByExp = Expression.Lambda(propertyAccess, parameter);
                resultExp = Expression.Call(typeof(Queryable), isAsc ? "OrderBy" : "OrderByDescending", new Type[] { typeof(TEntity), property.PropertyType }, tempData.Expression, Expression.Quote(orderByExp));
            }
            tempData = tempData.Provider.CreateQuery<TEntity>(resultExp);
            total = tempData.Count();
            return tempData.Skip<TEntity>(pageSize * (pageIndex - 1)).Take<TEntity>(pageSize).AsQueryable().ToList();
        }
        public virtual IList<TEntity> FindPageList<TEntity>(Expression<Func<TEntity, bool>> condition, string orderField, bool isAsc, int pageSize, int pageIndex, out int total) where TEntity : class
        {
            string[] _order = orderField.Split(',');
            MethodCallExpression resultExp = null;
            var tempData = this.Context.Set<TEntity>().Where(condition);
            foreach (string item in _order)
            {
                string _orderPart = item;
                _orderPart = Regex.Replace(_orderPart, @"\s+", " ");
                string[] _orderArry = _orderPart.Split(' ');
                string _orderField = _orderArry[0];
                bool sort = isAsc;
                if (_orderArry.Length == 2)
                {
                    isAsc = _orderArry[1].ToUpper() == "ASC" ? true : false;
                }
                var parameter = Expression.Parameter(typeof(TEntity), "t");
                var property = typeof(TEntity).GetProperty(_orderField);
                var propertyAccess = Expression.MakeMemberAccess(parameter, property);
                var orderByExp = Expression.Lambda(propertyAccess, parameter);
                resultExp = Expression.Call(typeof(Queryable), isAsc ? "OrderBy" : "OrderByDescending", new Type[] { typeof(TEntity), property.PropertyType }, tempData.Expression, Expression.Quote(orderByExp));
            }
            tempData = tempData.Provider.CreateQuery<TEntity>(resultExp);
            total = tempData.Count();
            return tempData.Skip<TEntity>(pageSize * (pageIndex - 1)).Take<TEntity>(pageSize).AsQueryable().ToList();
        }
        public virtual async Task<IList<TEntity>> FindPageListAsync<TEntity>(string orderField, bool isAsc, int pageSize, int pageIndex) where TEntity : class
        {
            string[] _order = orderField.Split(',');
            MethodCallExpression resultExp = null;
            var tempData = this.Context.Set<TEntity>().AsQueryable();
            foreach (string item in _order)
            {
                string _orderPart = item;
                _orderPart = Regex.Replace(_orderPart, @"\s+", " ");
                string[] _orderArry = _orderPart.Split(' ');
                string _orderField = _orderArry[0];
                bool sort = isAsc;
                if (_orderArry.Length == 2)
                {
                    isAsc = _orderArry[1].ToUpper() == "ASC" ? true : false;
                }
                var parameter = Expression.Parameter(typeof(TEntity), "t");
                var property = typeof(TEntity).GetProperty(_orderField);
                var propertyAccess = Expression.MakeMemberAccess(parameter, property);
                var orderByExp = Expression.Lambda(propertyAccess, parameter);
                resultExp = Expression.Call(typeof(Queryable), isAsc ? "OrderBy" : "OrderByDescending", new Type[] { typeof(TEntity), property.PropertyType }, tempData.Expression, Expression.Quote(orderByExp));
            }

            tempData = tempData.Provider.CreateQuery<TEntity>(resultExp).Skip<TEntity>(pageSize * (pageIndex - 1)).Take<TEntity>(pageSize).AsQueryable();
            return await tempData.ToListAsync();
        }
        public virtual async Task<IList<TEntity>> FindPageListAsync<TEntity>(Expression<Func<TEntity, bool>> condition, string orderField, bool isAsc, int pageSize, int pageIndex) where TEntity : class
        {
            string[] _order = orderField.Split(',');
            MethodCallExpression resultExp = null;
            var tempData = this.Context.Set<TEntity>().Where(condition);
            foreach (string item in _order)
            {
                string _orderPart = item;
                _orderPart = Regex.Replace(_orderPart, @"\s+", " ");
                string[] _orderArry = _orderPart.Split(' ');
                string _orderField = _orderArry[0];
                bool sort = isAsc;
                if (_orderArry.Length == 2)
                {
                    isAsc = _orderArry[1].ToUpper() == "ASC" ? true : false;
                }
                var parameter = Expression.Parameter(typeof(TEntity), "t");
                var property = typeof(TEntity).GetProperty(_orderField);
                var propertyAccess = Expression.MakeMemberAccess(parameter, property);
                var orderByExp = Expression.Lambda(propertyAccess, parameter);
                resultExp = Expression.Call(typeof(Queryable), isAsc ? "OrderBy" : "OrderByDescending", new Type[] { typeof(TEntity), property.PropertyType }, tempData.Expression, Expression.Quote(orderByExp));
            }
            tempData = tempData.Provider.CreateQuery<TEntity>(resultExp).Skip<TEntity>(pageSize * (pageIndex - 1)).Take<TEntity>(pageSize).AsQueryable();
            return await tempData.ToListAsync();
        }

        public async Task<int> FindPageListTotalAsync<TEntity>(Expression<Func<TEntity, bool>> condition) where TEntity : class
        {
            return await this.Context.Set<TEntity>().Where(condition).CountAsync();
        }

        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.Context.Dispose();
            }
            this.Context = null;
        }
    }
}
